/**
 * @file 场景控制接口实现
 *
 * 场景控制接口实现
 *
 * @author wq1977@gmail.com
 */

#include "ScenariosControl.h"
#include "app.h"
#include <wx/wfstream.h>
#include <wx/arrimpl.cpp>

extern const wxEventType wxEVT_SCENARIO_EXIT;
extern const wxEventType wxEVT_COMMAND_TERM_STATE_CHANGE;


DEFINE_EVENT_TYPE(wxEVT_SCENARIO_UPDATE)

WX_DEFINE_OBJARRAY(RepPieceArray);
IMPLEMENT_CLASS(ReportPieceStru, wxObject);

wxString TimeToStr(wxDateTime start){
  return wxString::Format(_T("%d-%d-%d %d:%d:%d %d"),
			  start.GetYear(),
			  start.GetMonth()+1,
			  start.GetDay(),
			  start.GetHour(),
			  start.GetMinute(),
			  start.GetSecond(),
			  start.GetMillisecond()
			  );
}

void DumpMsgKeyField(int Uid, osip_message_t *pomt)
{
  if (pomt){
    if (MSG_IS_REQUEST(pomt)){
      STT_TRACE(Uid,TL_MESSAGE,wxString::Format(_("Sip Request %s"),STT_LStr(pomt->sip_method)));
    }
    else{
      STT_TRACE(Uid,TL_MESSAGE,wxString::Format(_("Sip Response %s %d %s"),STT_LStr(pomt->cseq->method),pomt->status_code,
						STT_LStr(pomt->cseq->number)));
    }
  }
}

void SttEvt::Dump(int Uid){
	STT_TRACE(Uid,TL_MESSAGE,wxString::Format(_("Event Type: %d"),EventType));
	switch (EventType){
		case STTEVT_CASE_START:
		case STTEVT_TERM_REGSTATE_CHANGE:
			STT_TRACE(Uid,TL_MESSAGE,wxString::Format(_("Uid is %d"),GetUid()));
			break;
		case STTEVT_OSIP_EVENT:	{
			osip_message_t *pomt=GetOsipMsgPtr();
			DumpMsgKeyField(Uid,pomt);
		}
		break;
	}
}

void TermControl::DumpVarible(wxString prompt){
  printf("%s\n",(const char*) prompt.mb_str(wxConvUTF8));
  for(StrStrHash::iterator iter = Varibles.begin(); iter != Varibles.end(); iter++)
  {
    printf("%s: %s\n", (const char*) iter->first.mb_str(wxConvUTF8), (const char*)iter->second.mb_str(wxConvUTF8));
  }
  printf("CallInfo Varibles:\n");
  {
	  for(StrStrHash::iterator iter = pCallInfo->Varibles.begin(); iter != pCallInfo->Varibles.end(); iter++)
	  {
		printf("%s: %s\n", (const char*) iter->first.mb_str(wxConvUTF8), (const char*)iter->second.mb_str(wxConvUTF8));
	  }
  }
}

extern int UENetworkAuthorize(U8 *K, U8 *SQNms, U8 *RAND, U8 *AUTN, U8 *IK, U8 *CK, U8 *RES, U8 *AUTS);
extern int UEMD5Authorize(U8 *K, U8 *RAND, U8 *RES);
int TermControl::CaleResponse(std::string& Resp, std::string &Auts){
	/*if (lastnonce.size()<=32){
		STT_TRACE(Uid,TL_ERROR,wxString::Format(_("Bad Nonce Value [%s] size is:%d not great  than 32"),
							STT_LStr(lastnonce.c_str()),
							lastnonce.size()));
		return 3;
		}*/
  /*wxString Passwd=GetVaribleValue(wxT("__PASSWORD__"));
	std::string hs=Scenario::Conv2Std(Passwd);
	if ((hs.size()<(AU_MAX_K_LEN*2)) && (hs.size()>0)){
	  strcpy((char*)K,hs.c_str());
	  K[hs.size()]='0';
	  K[AU_MAX_K_LEN*2]=0;
	  }*/
  
	U8 RAND[33],AUTN[33];
	memset(RAND,0,sizeof(RAND));
	if (lastnonce.size()<=32){
        strcpy((char*)RAND,lastnonce.c_str());
	}
	else{
	    memcpy(RAND,lastnonce.c_str(),32);
        memset(AUTN,0,sizeof(AUTN));
        int cplen=32;
        if (cplen+32>(int)lastnonce.size()){
            cplen=lastnonce.size()-32;
        }
        memcpy(AUTN,&(lastnonce.c_str()[32]),cplen);
	}
	U8 RES[17];

#define AU_MAX_K_LEN    16
#define AU_MAX_RAND_LEN 16
#define AU_MAX_XRES_LEN 8
#define AU_MAX_RES_LEN  8
#define AU_MAX_CK_LEN   16
#define AU_MAX_IK_LEN   16
#define AU_MAX_AUTN_LEN 16
#define	AU_MAX_AUTS_LEN 14
#define AU_MAX_SQN_LEN  6
#define AU_MAX_MAC_LEN  8
#define AU_MAX_MACS_LEN 8
#define AU_MAX_AK_LEN   6
#define AU_MAX_AMF_LEN  2

	memset(RES,0,sizeof(RES));
	U8 IK[AU_MAX_IK_LEN*2+1]={0},CK[AU_MAX_CK_LEN*2+1]={0},AUTS[AU_MAX_AUTS_LEN*2+1]={0};
	int rspret;
	if ((strcmp(lastAlgorithm.c_str(), "MD5")==0) && (!wxGetApp().NoMd5)){
	    rspret=UEMD5Authorize(K, RAND, RES);
	}
	else if ((rspret=UENetworkAuthorize(K, (U8*)SQNms, RAND, AUTN, IK, CK, RES, AUTS))!=0){
		STT_TRACE(Uid,TL_ERROR,wxString::Format(_("Calc Response with K:%s Rand:%s Autn:%s return fail:%d!"),
								STT_LStr((char*)K),
								STT_LStr((char*)RAND),
								STT_LStr((char*)AUTN),
								rspret));
	}
	Resp = (const char*)RES;
	Auts=(const char*)AUTS;

	return rspret;

}

void TermControl::OnScenarioEnd(bool IsSucc, wxString Res){
    if (pCallInfo->GetCallState()==CALL_CB_ACTIVE){
	pCallInfo->SetCallState(CALL_CB_WAITEND);
	//now it has been change to restart time to do this
	//pCallInfo->Transactions.clear();
	Dialogs.clear();
	for (size_t i=0;i<pCallInfo->Timers.size();i++){
	    wxGetApp().CancelTimer(pCallInfo->Timers[i]);
	}
	pCallInfo->Timers.clear();

	if (controller->DecRunningScenarioNum(1,IsSucc,Res)){
	    pCallInfo->SetCallState(CALL_CB_WAITCASEEND);
	}
	controller->TouchReport(pCallInfo->pCurSce,Res);
	if (!IsSucc){
	    controller->TouchReport(pCallInfo->GetLog());
	}
	pCallInfo->ClearLog();
    }
}

osip_message_t *TermControl::GetCurrentMsg(SttEvt *evt){
	osip_message_t *pomt=NULL;
	if (evt){
		if (evt->GetEvtType() != STTEVT_OSIP_EVENT) return NULL;
		pomt=evt->GetOsipMsgPtr();
		if (!pomt){
			STT_TRACE(Uid,TL_ERROR,_("OSIP Event without sip message!"));
		}
	}
	else{
		pomt=ToSend;
	}
	return pomt;
}

wxString TermControl::GetVaribleValue(const wxString &key){
  if ((pCallInfo->Varibles.find(key) != pCallInfo->Varibles.end()) && (!pCallInfo->Varibles[key].IsEmpty())){
    return pCallInfo->Varibles[key];
  }
  else if ((Varibles.find(key) != Varibles.end()) && (!Varibles[key].IsEmpty())){
    return Varibles[key];
  }
  return wxT("");
}

void TermControl::SetVaribleValue(const wxString &name, wxString value){
  if ((Varibles.find(name) != Varibles.end()) &&(!Varibles[name].IsEmpty())){
    Varibles[name] = value;
  }
  else{
    pCallInfo->Varibles[name] = value;
  }
}

bool TermControl::CallIdMatch(const wxString &CallId, bool AllowAppend){
    wxString RCallId=GetVaribleValue(wxT("RegistCallId"));
    wxString NCallId=GetVaribleValue(wxT("NormalCallId"));
    if (RCallId == CallId) return true;
    if (NCallId == CallId) return true;
    if (pCallInfo->exCallIds.Index(CallId) != wxNOT_FOUND) return true;
    //this should happen,I forgot why put this code,but this really make bug during regist case execute!
    //commit it at 2006-10-17 :09:01
    /*if (NCallId.IsEmpty()){
      SetVaribleValue(wxT("NormalCallId"),CallId);
      return true;
      }*/

    if (AllowAppend){
	pCallInfo->exCallIds.push_back(CallId);
	return true;
    }
    STT_TRACE(Uid,TL_MESSAGE,wxString::Format(wxT("CallId Not Match: %s with %s or %s"),CallId.c_str(),RCallId.c_str(),NCallId.c_str()));

    return false;
}

void TermControl::SetRegisted(bool r){
    //printf("SetRegist %d %d\n",Uid,r);
    if (r!=Registed){
	Registed = r;
	controller->OnTermRegistStateChange(Uid,r);
    }
}

void TermControl::ClearRegistInfo(){
	Registed=false;
	RemotePort=0;
}


void TermControl::ClearDialogs(void){
    for(DialogPtrArray::iterator iter = Dialogs.begin(); iter != Dialogs.end(); iter++)
    {
        osip_dialog_free(*iter);
    }
    Dialogs.clear();
    Dialogs.Shrink();
}

osip_dialog_t *TermControl::GetDialog(osip_message_t *sip, int *Index){
    int i=0;
    for(DialogPtrArray::iterator iter = Dialogs.begin(); iter != Dialogs.end(); iter++)
    {
	if (((osip_dialog_t*)(*iter))->type == CALLER){
	    if (0==osip_dialog_match_as_uac(*iter,sip)){
		  if (Index) *Index=i;
			return *iter;
	    }
	}
	else{
	    if (0==osip_dialog_match_as_uas(*iter,sip)){
		  if (Index) *Index=i;
			return *iter;
			  }
		  }
	  i++;
		}
	  return NULL;
		}
		
		int TermControl::AddDialog(osip_dialog_t* dialog){
		  if (!dialog) return -1;
		  Dialogs.push_back(dialog);
			return Dialogs.size()-1;
			  }
			  
			  TermControl::~TermControl(){
    ClearDialogs();
}

void CallInfo::SetCallState(int s){
  wxMutexLocker lock(s_mutexProtectingState);
  static long IdSeed=1;
  State = s;
  if (s==CALL_CB_ACTIVE){
      startat=wxDateTime::UNow();
      id=IdSeed++;
  }
}

void CallInfo::LogString(const wxString&s){
    //    return;//TODO
  wxMutexLocker lock(s_mutexProtectingState);
  Log += (TimeToStr(wxDateTime::UNow()) + s + wxT("\n"));
}

void CallInfo::LogEvent(SttEvt *evt){
    //    return;//TODO
  wxMutexLocker lock(s_mutexProtectingState);
  Log+=TimeToStr(wxDateTime::UNow());
  Log+=wxT(" ");
  switch (evt->GetEvtType()){
  case STTEVT_TERM_REGSTATE_CHANGE:
    Log+=wxT("STTEVT_TERM_REGSTATE_CHANGE");
    break;
  case STTEVT_CASE_START:
      Log+=wxT("STTEVT_CASE_START")+wxString::Format(wxT(" %d"),evt->GetUid());
    break;
  case STTEVT_TIMEROUT_EVENT:
    Log+=wxT("STTEVT_TIMEROUT_EVENT: ");
    Log+=evt->GetTimerName();
    break;
  case STTEVT_OSIP_TIMEOUT:
    Log+=wxT("STTEVT_OSIP_TIMEOUT");
    break;
  case STTEVT_OSIP_EVENT:
    {
      Log+=wxT("STTEVT_OSIP_EVENT");
      char *msgP;
      size_t msgLen;
      osip_message_t *sip=evt->GetOsipMsgPtr();
      if (sip){
	int i;
	if((i = osip_message_to_str(sip, &msgP, &msgLen)) == 0){
	  Log+=wxT("\n");
	  Log+=STT_LStr(msgP);
	  osip_free(msgP);
	}
      }
    }
    break;
  default:
    Log+=wxString::Format(_("Unknown Event:%d"),evt->GetEvtType());
    break;
  }
  Log+=wxT("\n");
}

int CallInfo::GetCallState(){
  //wxMutexLocker lock(s_mutexProtectingState);
  return State;
}

bool TermControl::DoDefaultProcess(SttEvt *evt){
	switch (evt->GetEvtType()){
		case STTEVT_TERM_REGSTATE_CHANGE:
		  return true;
		case STTEVT_CASE_START:
	case STTEVT_SCEARIO_FINISH:
	case STTEVT_CASE_FINISH:
		  //pCallInfo->ClearLog();
		  return true;
		case STTEVT_OSIP_TIMEOUT:
		  if (pCallInfo->pCurSce->EndOnOsipTimeout){
		    OnScenarioEnd(false,_("Osip Transaction Timeout"));
		  }
		  return true;
		case STTEVT_OSIP_EVENT:
		    break;
	}
	STT_TRACE(Uid,TL_MESSAGE,wxT("Do Default Return false"));
	return false;
}

extern int ExSockStartPort;
int TermControl::GetLocalPort(){
    return ExSockStartPort+(Uid % MAX_EX_SOCK_NUM);
}

void TermControl::Trace(int Level, const wxString& Value){
	wxNode *node = Traces.GetFirst();
	while (node)
	{
		TraceForm *win = (TraceForm *)node->GetData();
		win->Trace(Level, Value);
		node = node->GetNext();
	}
	//Add Error Print to the report
	if (pCallInfo){
	    pCallInfo->LogString(Value);
	}
}

TermControl *ScenarioControlThread::GetTermInstFromEvt(SttEvt *evt){
	TermControl *ret=NULL;
	if (!evt){
		STT_TRACE(-1,TL_ERROR,_("Bad Event, Null Pointer when recv event!!"));
		return NULL;
	}
	switch (evt->GetEvtType()){
	case STTEVT_TIMEROUT_EVENT:
	case STTEVT_CASE_START:
	case STTEVT_TERM_REGSTATE_CHANGE:
	  {
	    int Uid=evt->GetUid();
	    size_t TermId=Uid %MAX_TERM_NUM;
	    if ((controller)&&(TermId <controller->AllTerms.size())){
	      ret = controller->AllTerms[TermId];
	    }
	  }
	  break;
		case STTEVT_OSIP_TIMEOUT:
		case STTEVT_OSIP_EVENT:{
		  //TODO Need switch transaction type to judge we need check from or to
				osip_message_t *sip = evt->GetOsipMsgPtr();
				if (!sip){
					STT_TRACE(-1,TL_ERROR,_("Bad Event, Null Pointer when recv event!!"));
					return NULL;
				}
				int Uid= GetUidFromSipMsg(sip,STT_MSG_INCOMMING);
				size_t TermId=Uid %MAX_TERM_NUM;
				if ((controller)&&(TermId <controller->AllTerms.size())){
					ret = controller->AllTerms[TermId];
				}
		    }
			break;

	}
	return ret;
}

void ScenarioControlThread::ProcessSttEvt(SttEvt *evt,TermControl *TermInst){
        if (wxGetApp().ApplicationExit) return;
	int MatchCount=0;
	if (!TermInst){
		STT_TRACE(-1,TL_ERROR,_("Bad Event,No Term Instence Matched!!"));
		return;
	}

	if (evt->GetEvtType()==STTEVT_CASE_START){
	  TermInst->pCallInfo->ClearLog();
	  TermInst->pCallInfo->LogString(wxString::Format(_("Scenario %s Start at:%s"),
							  TermInst->pCallInfo->pCurSce->GetIdentifier().c_str(),
							  TimeToStr(wxDateTime::UNow()).c_str()));
	}
	else if ((evt->GetEvtType()==STTEVT_OSIP_EVENT)
		 || (evt->GetEvtType()==STTEVT_OSIP_TIMEOUT)) {
	  osip_message_t *pomt=evt->GetOsipMsgPtr();
	  char *callidstr=NULL;
	  if ((!MSG_IS_NOTIFY(pomt)) &&(osip_call_id_to_str(pomt->call_id,&callidstr)==0)){
	      if (!TermInst->CallIdMatch(STT_LStr(callidstr))){
		  osip_free(callidstr);
		  STT_TRACE(TermInst->Uid,TL_ERROR,_("Bad Event,Sip Event has bad callid, not belong to this call!!"));
		  if ((evt->GetEvtType()==STTEVT_OSIP_EVENT) &&(MSG_IS_REQUEST(pomt))){
		      //if we do not do this, the transaction will never timeout!
		      wxGetApp().AllTranses.RemoveTrans(evt->GetOsipTransPtr());
		  }
		  return;
	      }
	      osip_free(callidstr);
	  }
	  if (evt->GetEvtType()==STTEVT_OSIP_EVENT){
	      TermInst->GetController().TouchReport(STT_MSG_INCOMMING,pomt);
	  }
	}
	else if (evt->GetEvtType()==STTEVT_TIMEROUT_EVENT){
	    if (evt->GetTimerIdent() != TermInst->pCallInfo->GetId()){
		STT_TRACE(TermInst->Uid,TL_ERROR,_("Bad Event, Timerout Event has bad Id, not belong to this call!!"));
		return;
	    }
	}

        TermInst->pCallInfo->LogEvent(evt);


	evt->Dump(TermInst->Uid);
	MatchCount+=TermInst->pCallInfo->pCurSce->ProcessSttEvt(*TermInst,evt);
	if (wxGetApp().m_ScenariosManager->DefaultScenario && TermInst->pCallInfo->pCurSce->GetRunDefaultRule()){
		MatchCount+=wxGetApp().m_ScenariosManager->DefaultScenario->ProcessSttEvt(*TermInst,evt);
	}
	bool DefaultSucc=TermInst->DoDefaultProcess(evt);
	  if ((MatchCount==0) &&(!DefaultSucc) &&(TermInst->pCallInfo->GetCallState() ==CALL_CB_ACTIVE)){
		STT_TRACE(TermInst->Uid,TL_ERROR,_("Undefined Event,Case Fail!!"));
		evt->Dump(TermInst->Uid);
		TermInst->OnScenarioEnd(false,_("Undefined Event"));
		return;
	}

	if ((evt->GetEvtType()!=STTEVT_SCEARIO_FINISH) &&(evt->GetEvtType()!=STTEVT_CASE_FINISH)){
	    if (TermInst->pCallInfo->GetCallState()==CALL_CB_WAITEND){
		TermInst->pCallInfo->SetCallState(CALL_CB_WAITSTART);

		if (controller->FinalStyle==1){
		    SttEvt ev( (int)TermInst->Uid, (int)STTEVT_SCEARIO_FINISH);
		    ProcessSttEvt(&ev, TermInst);
		}

	    }
	    else if (TermInst->pCallInfo->GetCallState()==CALL_CB_WAITCASEEND){
		TermInst->pCallInfo->SetCallState(CALL_CB_WAITSTART);

		if (controller->FinalStyle==1){
		    SttEvt ev( (int)TermInst->Uid, (int)STTEVT_SCEARIO_FINISH);
		    ProcessSttEvt(&ev, TermInst);
		}
		else if (controller->FinalStyle==2){
		    STT_TRACE(-1,TL_MESSAGE,wxString::Format(wxT("Case Finish Message Was genurated by Term:%d"),TermInst->Uid));

		    //Now It's time to let all the call CB execute quit rule
		    for (int CurIdx=0;CurIdx<(int)controller->AllCallInfos.size();CurIdx++){
			int CurTermIndex=controller->AllCallInfos[CurIdx]->UserIndexStart % MAX_TERM_NUM;
			int Uid=controller->AllTerms[CurTermIndex]->Uid;
			wxGetApp().DisPatchEvt(Uid, new SttEvt(Uid,STTEVT_CASE_START));
			SttEvt ev(Uid, (int)STTEVT_CASE_FINISH);
			ProcessSttEvt(&ev, controller->AllTerms[CurTermIndex]);
		    }
		}
	    }
	}

}

void ScenarioControlThread::ProcessSttEvt(SttEvt *evt){
    TermControl *TermInst = GetTermInstFromEvt(evt);
    ProcessSttEvt(evt,TermInst);
}

extern void ProcessSipBuffer(char*msg,int n,sockaddr*cliaddr/*,int UdpSocked, sockaddr*cliaddr,int len*/);
void *ScenarioControlThread::Entry(){
#define MAX_SIP_RAW_PROCESS_NUM     5
#define MAX_PROCESS_NUM_BEFORE_POLL 10
    while (true){

      for (int i=0;i<MAX_PROCESS_NUM_BEFORE_POLL;i++){
	SttEvt *evt=m_EvtQueue.pop();
	if (evt){
	    ProcessSttEvt(evt);
	    delete(evt);
	}
	else{
	    break;
	}
      }

      //Poll Osip Stack
      osip_ict_execute(wxGetApp().osip);
      osip_ist_execute(wxGetApp().osip);
      osip_nict_execute(wxGetApp().osip);
      osip_nist_execute(wxGetApp().osip);
      osip_timers_ict_execute(wxGetApp().osip);
      osip_timers_ist_execute(wxGetApp().osip);
      osip_timers_nict_execute(wxGetApp().osip);
      osip_timers_nist_execute(wxGetApp().osip);

	  {
		  for (int i=0;i<MAX_SIP_RAW_PROCESS_NUM;i++){
			  SipRawMsg *evt=wxGetApp().sipQueue.pop();
			  if (evt){
				  ProcessSipBuffer(evt->buf,evt->len,&evt->from);
				  delete(evt);
			  }
			  else{
				  break;
			  }
		  }
	  }

      //Free Memory of dead transaction
      wxGetApp().AllTranses.FreeDeadTrans();

      //It seems that timer_c and timer_f not implement in the stack.
      //we do it here.
      osip_transaction_t *transaction;
      osip_list_iterator_t iterator;
      osip_list_t * alllist[2]={
	  wxGetApp().osip->osip_ist_transactions,
	  wxGetApp().osip->osip_nist_transactions
      };
	  {
		  for (int i=0;i<2;i++){
			  int len;
			  len = osip_list_size (alllist[i]);
			  if ( len > 0){
				  transaction =
				  (osip_transaction_t *) osip_list_get_first (alllist[i],  &iterator);
				  while (osip_list_iterator_has_elem (iterator))  {
				  time_t now = time (NULL);
				  if (difftime(now,transaction->birth_time)>180){ //3 minutes
					  wxGetApp().AllTranses.RemoveTrans(transaction,false);
				  }
				  transaction = (osip_transaction_t *) osip_list_get_next (&iterator);
				  }
			  }
		  }
	  }

      if ((m_EvtQueue.depth()==0) && (wxGetApp().sipQueue.depth()==0)){
	    wxMilliSleep(100);
      }

      if (controller->Terminated){
	break;
      }
    }

    m_EvtQueue.clear();
    return NULL;
}

#define STT_QUEUEDEPTH_VS_THREAD_NUM 2
#define STT_MAX_PROCESS_THREAD_NUM   1
#define STT_MIN_PROCESS_THREAD_NUM   1
ScenarioControlThreadPool::~ScenarioControlThreadPool(){
    CtrlThreadArray::iterator iter;
    for (iter=pool.begin();iter!=pool.end();iter++){
	ScenarioControlThread*p = *iter;
	p->Wait();
	delete(p);
    }
    pool.clear();
    pool.Shrink();
}

void *ScenarioControlThreadPool::Entry(){
    //    for (int i=0;i<ThreadNum;i++){
    pool.push_back(new ScenarioControlThread(controller));
    wxMilliSleep(100);//Wait ScenarioControlThread Finish Init
    //}
    //wxDebugContext::SetCheckpoint();

    while (true){
	if (controller->GetTotalLeft()>0) {
	    controller->TestStartCallGroup();
	}
	else{
	    //All the Case finish,we need wait all the transaction being removed from stack
	    if (controller->IsAllTransRemoved()){
		wxCommandEvent ScenarioExitEvt(wxEVT_SCENARIO_EXIT);
		wxPostEvent(controller->GetEvtHandler(), ScenarioExitEvt);
		break;
	    }
	}

	//Check if System memory is too low
	/* Now We use SipTestTool Daemon to handle this.
	if (wxGetApp().NeedStopForMemNotEnough()){
	    STT_TRACE(-1,TL_ERROR,_("System free memory is almost finish, exit the test!"));
	    wxCommandEvent ScenarioExitEvt(wxEVT_SCENARIO_EXIT);
	    wxPostEvent(controller->GetEvtHandler(), ScenarioExitEvt);
	    break;
	 }
	*/

	//Check if the first 5 transaction in controller has been dead and remove
	controller->TestAndRemoveDeadTrans(5);

	wxMilliSleep(500);
	if (controller->Terminated){
	    break;
	}
    }
    return NULL;
}

SttScenarioControl::SttScenarioControl(ScenariosForm *eh, int CaseStyle, int TermNum, int totaltimes,
				       int calldur, int callhold, int callheavy, int finalStyle){
    FinalStyle=finalStyle;
	threadpool=NULL;
	CurScenario = NULL;
	Terminated=false;
	CurRoundTestSceIndex=0;
	TestStyle = CaseStyle;
	OriTotal=TotalTimes=totaltimes;
	CallDuration=calldur;
	CallHold=callhold;
	SafeStoped=false;
	CallHeavy=callheavy;
	Handle = eh;
	Handle->SuccScenariosNum=Handle->FailScenariosNum=Handle->RunningScenariosNum=0;
	Handle->ErrStatistic.clear();
	AllTerms.clear();
	LastIdleCallIdx=0;
	LastTestTime = wxDateTime::UNow() - wxTimeSpan(0,0,100,0);
	int CallGroupNum = TermNum/wxGetApp().m_ScenariosManager->GetMaxTermNeeded();
	for (int i=0;i<CallGroupNum;i++){
	  CallInfo *p=new CallInfo(i);
	  p->SetCallState(CALL_CB_IDLE);
	  p->pCurSce = NULL;
	  //s->InitCallVaribles(p->Varibles);
	  p->UsedTermNum = wxGetApp().m_ScenariosManager->GetMaxTermNeeded();
	  p->UserIndexStart = eh->Sf_Id()*MAX_TERM_NUM+i*p->UsedTermNum;
	  AllCallInfos.push_back(p);
	}

	report=new SttReport();

	{
		for (int i=0;i<TermNum;i++){
		  TermControl* p=new TermControl(this,eh->Sf_Id()*MAX_TERM_NUM+i);
		  //s->InitTermVaribles(p->Varibles);

		  wxNode *node = eh->Tracers.GetFirst();
		  while (node)
			{
			  TraceForm *win = (TraceForm *)node->GetData();
			  if (win->IsTracingMe(i)){
				p->AppendTracer(win);
			  }
			  node = node->GetNext();
			}
		  if (i<CallGroupNum*wxGetApp().m_ScenariosManager->GetMaxTermNeeded()){
			p->pCallInfo = AllCallInfos[i/wxGetApp().m_ScenariosManager->GetMaxTermNeeded()];
		  }
		  else{
			p->pCallInfo = NULL;
		  }
		  AllTerms.push_back(p);
		}
	}

	threadpool = new ScenarioControlThreadPool(this,CallGroupNum/300+1);
}

static wxMutex s_mutexProtectingReport;
void SttScenarioControl::DoReport(){
  wxMutexLocker lock(s_mutexProtectingReport);
  report->SaveToReport();
  Handle->LastReportPath = GetReportFileName().GetFullPath();

}

bool SttScenarioControl::AppendEvent(SttEvt*pstevt, int Uid){
    TermControl *t=wxGetApp().GetTermIns(Uid);
    if (t){
	if (threadpool){
	    ScenarioControlThread* thread= threadpool->ThreadItem(t->pCallInfo->MyIndex % threadpool->depth());
	    thread->m_EvtQueue.push_back(pstevt);
	    if (thread->m_EvtQueue.depth()>(size_t)thread->MaxDepth){
		thread->MaxDepth = thread->m_EvtQueue.depth();
		TouchReport(_("Max Execute Thread Queue Depth"),thread->MaxDepth);
	    }
	    return true;
	}
    }
    return false;
}

bool SttScenarioControl::SetSafeStop(bool Stop){
  SafeStoped=Stop;
  if (SafeStoped){
    return (Handle->RunningScenariosNum==(Handle->SuccScenariosNum+Handle->FailScenariosNum));
  }
  return false;
}

SttScenarioControl::SttScenarioControl(ScenariosForm *eh, Scenario* s, int TermNum, int totaltimes,
				       int calldur, int callhold, int callheavy,int finalStyle){
    FinalStyle=finalStyle;
	threadpool=NULL;
	Terminated=false;
	CurScenario = s;
	OriTotal=TotalTimes=totaltimes;
	CallDuration=calldur;
	TestStyle = 0;
	CurRoundTestSceIndex=0;
	CallHold=callhold;
	CallHeavy=callheavy;
	SafeStoped=false;
	Handle = eh;
	Handle->SuccScenariosNum=Handle->FailScenariosNum=Handle->RunningScenariosNum=0;
	Handle->ErrStatistic.clear();
	AllTerms.clear();
	LastIdleCallIdx=0;
	LastTestTime = wxDateTime::UNow() - wxTimeSpan(0,0,100,0);
	int CallGroupNum = TermNum/s->GetNeedTermNum();
	for (int i=0;i<CallGroupNum;i++){
		CallInfo *p=new CallInfo(i);
		p->SetCallState(CALL_CB_IDLE);
		p->pCurSce = s;
		s->InitCallVaribles(p->Varibles);
		p->UsedTermNum = s->GetNeedTermNum();
		p->UserIndexStart = eh->Sf_Id()*MAX_TERM_NUM+i*s->GetNeedTermNum();
		AllCallInfos.push_back(p);
	}

	report=new SttReport();

	{
		for (int i=0;i<TermNum;i++){
			TermControl* p=new TermControl(this,eh->Sf_Id()*MAX_TERM_NUM+i);
			s->InitTermVaribles(p->Varibles,true);

			wxNode *node = eh->Tracers.GetFirst();
			while (node)
			{
				TraceForm *win = (TraceForm *)node->GetData();
				if (win->IsTracingMe(i)){
					p->AppendTracer(win);
				}
				node = node->GetNext();
			}
			if (i<CallGroupNum*s->GetNeedTermNum()){
				p->pCallInfo = AllCallInfos[i/s->GetNeedTermNum()];
			}
			else{
				p->pCallInfo = NULL;
			}
			AllTerms.push_back(p);
		}
	}

	threadpool = new ScenarioControlThreadPool(this, CallGroupNum/300+1);
}

void SttScenarioControl::OnTermRegistStateChange(int TermId, bool newv){
	wxGetApp().DisPatchEvt(TermId, new SttEvt(TermId,STTEVT_TERM_REGSTATE_CHANGE));
	if ((wxGetApp().frame)&&(wxGetApp().frame->gtMenu)&&(wxGetApp().frame->gtMenu->IsChecked(Minimal_GLOBAL_SHOW_REGSTATE))){
	    size_t index=TermId % MAX_TERM_NUM;
	    if (index<AllTerms.size()){
		wxColour c=*wxBLACK;
		if (newv){
		    c=*wxBLUE;
		}
		int r=index/Handle->m_TerminalStateGrid->GetNumberCols();
		int col=index%Handle->m_TerminalStateGrid->GetNumberCols();

		wxTermStateChangeEvent event(r,col,c,wxEVT_COMMAND_TERM_STATE_CHANGE, -1);
		event.SetEventObject(Handle->m_TerminalStateGrid);
		wxPostEvent(Handle->m_TerminalStateGrid,event);

	    }
	}
}

void SttScenarioControl::TestAndRemoveDeadTrans(int Num){
    if (Num<0) Num=AllTrans.size();
    if (Num>(int)AllTrans.size()) Num=AllTrans.size();
    for (int i=0;i<Num;i++){
	if (!wxGetApp().AllTranses.IsAlive(AllTrans[i])){
	    AllTrans.erase(AllTrans.begin()+i);
	}
    }
    return;
}

bool SttScenarioControl::IsAllTransRemoved(){
    for (size_t i=0;i<AllTrans.size();i++){
	if (wxGetApp().AllTranses.IsAlive(AllTrans[i])){
	    return false;
	}
    }
    return true;
}

void SttScenarioControl::AddRunningScenarioNum(int num){

	Handle->RunningScenariosNum+=num;
	//This way is fast but the evt may lose or delay.
	wxCommandEvent ScenarioUpdateEvt(wxEVT_SCENARIO_UPDATE);
	wxPostEvent(GetEvtHandler(), ScenarioUpdateEvt);

	if (((Handle->SuccScenariosNum+Handle->FailScenariosNum) >=Handle->RunningScenariosNum) &&(threadpool)){
	    //TOREMOVE
	    //wxCommandEvent ScenarioExitEvt(wxEVT_SCENARIO_EXIT);
	    //wxPostEvent(GetEvtHandler(), ScenarioExitEvt);

	    //This way is directly without delay!!
	    //after the delay,maybe I have been killed!!
	    //Handle->m_ScenariosResultGrid->UpdateState();
	}
}


/**
 * Find Idle CallCb To Start Call
 *
 * 从上次检查结果开始,检查是否可以可以新的呼叫.
 * 如果呼叫控制块状态为IDLE.
 *     如果场景要求自动注册 发送每个节点的注册消息
 */
void SttScenarioControl::TestStartCallGroup(void){
    wxDateTime now= wxDateTime::UNow();
    //if (TotalTimes==0) return;
    //if (SafeStoped) return;

    //if the program is too busy to process
    if (wxGetApp().MyselfTooBusy()){
	return;
    }

    if (LastTestTime<(now-wxTimeSpan(0,0,CallDuration,0))){
	int heavy=CallHeavy;
	int i;

	//some debug code put here for execute every Duration time
	//wxDebugContext::PrintStatistics();
	//wxDebugContext::Dump();

	for (i=0;i<(int)AllCallInfos.size();i++){
	    int CurIdx= (i+LastIdleCallIdx) % AllCallInfos.size();
	    bool ForceInit=false;
	    if ((AllCallInfos[CurIdx]->GetCallState()==CALL_CB_IDLE) ||
		(AllCallInfos[CurIdx]->GetCallState()==CALL_CB_WAITSTART)){

		int CurTermIndex=AllCallInfos[CurIdx]->UserIndexStart % MAX_TERM_NUM;
		bool PassFinalMarkCheck=true;
		for (int i=0;i<AllCallInfos[CurIdx]->UsedTermNum;i++){
		    if (AllTerms[CurTermIndex+i]->FinalMark){
			PassFinalMarkCheck=false;
			break;
		    }
		}
		if (!PassFinalMarkCheck) continue;

		if (TotalTimes==0) continue;
		if (SafeStoped) continue;

		switch (TestStyle){
		case 0:
		    AllCallInfos[CurIdx]->pCurSce = CurScenario;
		    break;
		case 1:
		    if (CurRoundTestSceIndex>=(int)wxGetApp().m_ScenariosManager->AllScenarios.size()){
			TotalTimes=0;
			return;
		    }
		    //ForceInit=true;
		    AllCallInfos[CurIdx]->pCurSce = wxGetApp().m_ScenariosManager->AllScenarios[CurRoundTestSceIndex];
		    CurRoundTestSceIndex++;
		    break;
		case 2:{
		    //ForceInit=true;
		    int  randindex = rand() % wxGetApp().m_ScenariosManager->AllScenarios.size();
		    AllCallInfos[CurIdx]->pCurSce = wxGetApp().m_ScenariosManager->AllScenarios[randindex];
		}
		    break;
		}

		AllCallInfos[CurIdx]->SetCallState(CALL_CB_ACTIVE);
		AllCallInfos[CurIdx]->pCurSce->InitCallVaribles(AllCallInfos[CurIdx]->Varibles);

		//If the transaction mark as removed manully,we remove it now!
		for (StrTransPtrHash::iterator iter=AllCallInfos[CurIdx]->Transactions.begin();
		     iter!=AllCallInfos[CurIdx]->Transactions.end();iter++){
		    if (iter->first.StartsWith(wxT("_"))){
			wxGetApp().AllTranses.RemoveTrans(iter->second);//=false;
		    }
		}

		AllCallInfos[CurIdx]->Transactions.clear();
		AllCallInfos[CurIdx]->exCallIds.clear();

		//AllCallInfos[CurIdx]->Dialogs.clear();
		//wxGetApp().CancelScenarioTimer(GetEvtHandler()->FormIndex);
		AddRunningScenarioNum();
		int Uid=AllTerms[CurTermIndex]->Uid;
		{
			for (int i=0;i<AllCallInfos[CurIdx]->UsedTermNum;i++){
				//AllTerms[CurTermIndex+i]->ClearRegistInfo();
				AllTerms[CurTermIndex+i]->ClearDialogs();
				AllCallInfos[CurIdx]->pCurSce->InitTermVaribles(AllTerms[CurTermIndex+i]->Varibles,ForceInit);
				if (AllTerms[CurTermIndex+i]->GetVaribleValue(wxT("RegistState"))==wxT("Idle")){
				AllTerms[CurTermIndex+i]->RemotePort=0;
				}
			}
		}
		STT_TRACE(Uid,TL_MESSAGE,wxString::Format(_("Scenario %s Start at Term:%d"),AllCallInfos[CurIdx]->pCurSce->GetIdentifier().c_str(),Uid));
		wxGetApp().DisPatchEvt(Uid, new SttEvt(Uid,STTEVT_CASE_START));
		/*if (wxGetApp().m_ScenariosManager.DefaultScenarioit){
		  wxGetApp().m_ScenariosManager.DefaultScenario->ExecuteDefaultAction((*AllTerms[CurTermIndex]));
		  }
		  CurScenario->ExecuteDefaultAction((*AllTerms[CurTermIndex]));*/

		heavy--;
		if (TotalTimes>0) TotalTimes--;
		if (TotalTimes==0) break;
		if (heavy==0) break;
	    }
	    else{
		int CurTermIndex=AllCallInfos[CurIdx]->UserIndexStart;
		TermControl *t=wxGetApp().GetTermIns(CurTermIndex);
		if (t){
		    if (AllCallInfos[CurIdx]->GetStartTime()<(now-wxTimeSpan(0,0,t->pCallInfo->pCurSce->GetTimeout(),0))){
			//TODO
			wxGetApp().DumpTimer(t->Uid);
			t->OnScenarioEnd(false,_("Scenario Timeout"));
			t->pCallInfo->SetCallState(CALL_CB_IDLE);
		    }
		    for (int i=0;i<AllCallInfos[CurIdx]->UsedTermNum;i++){
			wxGetApp().GetTermIns(CurTermIndex+i)->FinalMark=false;
		    }
		}
	    }
	}
	LastIdleCallIdx = (i+LastIdleCallIdx+1) % AllCallInfos.size();
	LastTestTime = wxDateTime::UNow();
    }
}


void SttScenarioControl::TouchReportStatistic(wxString Key){
    wxMutexLocker lock(s_mutexProtectingReport);
    report->TouchReportStatistic(Key);
}

void SttScenarioControl::TouchReport(wxString MaxKey, long MaxValue){
    wxMutexLocker lock(s_mutexProtectingReport);
    report->TouchReport(MaxKey,MaxValue);
}

void SttScenarioControl::TouchReport(wxString SceLog){
    //    return;//TODO
  wxMutexLocker lock(s_mutexProtectingReport);
  report->TouchReport(SceLog);
}

void SttScenarioControl::TouchReport(int msgtype,osip_message_t* sip){
    //    return;//TODO
  wxMutexLocker lock(s_mutexProtectingReport);
  report->TouchReport(msgtype,sip);
}

void SttScenarioControl::TouchReport(Scenario* s, wxString Result){
    //    return;//TODO
  wxMutexLocker lock(s_mutexProtectingReport);
  report->TouchReport(s,Result);
}

/**
 * Inform Scenario End
 *
 */
bool SttScenarioControl::DecRunningScenarioNum(int num, bool IsSucc, wxString Res){
  if (IsSucc) {
    Handle->SuccScenariosNum++;
  }
  else{
    Handle->FailScenariosNum++;
    if (Handle->ErrStatistic.find(Res) != Handle->ErrStatistic.end()){
      Handle->ErrStatistic[Res] = Handle->ErrStatistic[Res] + 1;
    }
    else{
      Handle->ErrStatistic[Res] = 1;
    }

  }



    //This way is fast but the evt may lose or delay.
    wxCommandEvent ScenarioUpdateEvt(wxEVT_SCENARIO_UPDATE);
    wxPostEvent(GetEvtHandler(), ScenarioUpdateEvt);

    if (((OriTotal==(Handle->SuccScenariosNum+Handle->FailScenariosNum)) &&(threadpool)) ||
	((Handle->m_ScenariosControlPanel->IsFailOnStop()) &&(Handle->FailScenariosNum>0)) ||
	(SafeStoped &&(Handle->RunningScenariosNum==(Handle->SuccScenariosNum+Handle->FailScenariosNum))))
      {
	if ((Handle->m_ScenariosControlPanel->IsSafeStop()) && (Handle->RunningScenariosNum>(Handle->SuccScenariosNum+Handle->FailScenariosNum))){
	  SafeStoped=true;
	}
	else {
	    //This way is directly without delay!!
	    //after the delay,maybe I have been killed!!
	    //Handle->m_ScenariosResultGrid->UpdateState();
	    return true;
	}
    }
    return false;
};

int SttScenarioControl::GetTotalLeft(){
  if (OriTotal>0) {
      return (int) (!((TotalTimes==0)&&(Handle->SuccScenariosNum+Handle->FailScenariosNum==Handle->RunningScenariosNum)));
  }
  else{
    if ((SafeStoped) && (Handle->SuccScenariosNum+Handle->FailScenariosNum==Handle->RunningScenariosNum)){
      return 0;
    }
    else{
      return 1;
    }
  }
};

TermControl *SttScenarioControl::GetTermCtrl(int Index){
    Index %= MAX_TERM_NUM;
    if ((Index<0) || (Index>=(int)AllTerms.size())) return NULL;
    return AllTerms[Index];
}

SttReport::SttReport(){
  start = wxDateTime::UNow();
  SubReportPiece st;
  st.FirstFail=false;
  st.TotalRun = 0;
  st.TotalSucc = 0;
  st.TotalFail = 0;
  st.FirstFailReason=wxT("");
  st.FirstFailTime = wxDateTime::UNow();
  stGlobal=st;
  GlobalFirstFail=NULL;

}

void SttReport::TouchReport(wxString MaxKey, long MaxValue){
    NumDateStru st;
	st.Num=MaxValue;
	st.time =wxDateTime::UNow();
    MaxValus[MaxKey] = st;
}

void SttReport::TouchReport(wxString SceLog){
    if (SceLogs.size()<MAX_SCENARIOS_LOG){
	SceLogs.push_back(SceLog);
    }
}

void SttReport::TouchReportStatistic(wxString Key){
    if (UserStatistic.find(Key) != UserStatistic.end()){
	UserStatistic[Key]++;
    }
    else{
	UserStatistic[Key]=1;
    }
}

void SttReport::TouchReport(int msgtype, osip_message_t* sip){
    if (!sip) return;
    wxString Key;
    if (MSG_IS_REQUEST(sip)){
	if (MSG_IS_REGISTER(sip)){
	    osip_header_t *head=NULL;
	    osip_message_get_expires(sip,0,&head);
	    if ((head) &&(head->hvalue)){
		int e=atoi(head->hvalue);
		if (e>0){
		    Key=wxT("Register");
		}
		else{
		    Key=wxT("DeRegister");
		}
	    }
	    else{
		Key=wxT("BadRegister");
	    }
	}
	else if (MSG_IS_SUBSCRIBE(sip)){
	    osip_header_t *head;
	    osip_message_get_expires(sip,0,&head);
	    if ((head) &&(head->hvalue)){
		int e=atoi(head->hvalue);
		if (e>0){
		    Key=wxT("Subscribe");
		}
		else{
		    Key=wxT("DeSubscribe");
		}
	    }
	    else{
		Key=wxT("BadSubscribe");
	    }
	}
	else{
	    Key=STT_LStr(sip->sip_method);
	}
    }
    else {
	Key=STT_LStr(sip->cseq->method);
	Key+=wxString::Format(wxT(" Rsp(%d)"),sip->status_code);
    }
    switch (msgtype){
    case STT_MSG_INCOMMING:
	if (RcvMsgStatistic.find(Key) == RcvMsgStatistic.end()){
	    RcvMsgStatistic[Key]=1;
	}
	else{
	    RcvMsgStatistic[Key]++;
	}
	break;
    default:
	if (SntMsgStatistic.find(Key) == SntMsgStatistic.end()){
	    SntMsgStatistic[Key]=1;
	}
	else{
	    SntMsgStatistic[Key]++;
	}
	break;
    }
}

void SttReport::TouchReport(Scenario* s, wxString Result){

    if (ret.find(s) == ret.end()){
	SubReportPiece stPiece;
	stPiece.FirstFail=false;
	stPiece.TotalRun = 0;
	stPiece.TotalSucc = 0;
	stPiece.TotalFail = 0;
	stPiece.FirstFailReason = wxT("");
	stPiece.FirstFailTime = wxDateTime::UNow();
	ret[s] = stPiece;
    }
    stGlobal.TotalRun++;
    ret[s].TotalRun++;
    if (Result==wxT("Succ")){
	stGlobal.TotalSucc++;
	ret[s].TotalSucc++;
    }
    else{
      ret[s].TotalFail++;
      stGlobal.TotalFail++;
      if (!ret[s].FirstFail){
	  ret[s].FirstFail=true;
	  ret[s].FirstFailReason = Result;
	  ret[s].FirstFailTime = wxDateTime::UNow();
      }
      if (!stGlobal.FirstFail){
	  stGlobal.FirstFail=true;
	  GlobalFirstFail = s;
	  stGlobal.FirstFailReason = Result;
	  stGlobal.FirstFailTime = wxDateTime::UNow();
      }
    }
};

extern void DoGlobalMsgStatReport(std::string &buf);
void SttReport::SaveToReport(){

    if (wxGetApp().ReportPath.IsEmpty()){
	Base =wxFileName(wxGetCwd()+wxFileName::GetPathSeparator()+wxT("report")+wxFileName::GetPathSeparator()+wxT("report.html"));
    }
    else{
	Base = wxGetApp().ReportPath;
    }
  if (!Base.DirExists(Base.GetPath())){
    Base.Mkdir(Base.GetPath());
  }
  wxFileOutputStream f(Base.GetFullPath());
  std::string buf="<html><title>Sip Test Tool Report</title>";
  buf+="<STYLE TYPE=\"text/css\">\n";
  buf+="<!--\n";
  buf+="	pre {\n		background:#EEE;\n 		padding: 2mm;\n		border:1px solid #999;\n		}\n";
  buf+="-->\n";
  buf+="</STYLE> \n";

#ifdef WIN32
  buf+="<meta http-equiv=\"Content-Type\" content=\"text/html; charset=gb2312\" />";
#else
  buf+="<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />";
#endif

  buf+="<body>";
  buf+=Scenario::Conv2Std(_("<h1>Report</h1>"));
  buf+=Scenario::Conv2Std(_("<h2>Global Report</h2>"));
  wxString startstr=TimeToStr(start);
  buf+=Scenario::Conv2Std(wxString::Format(_("Test Start At:%s <p>"),startstr.c_str()));
  buf+="<table cellpadding=\"7\" cellspacing=\"1\" border=\"1\" width=\"100%\">";
  buf+=Scenario::Conv2Std(_("<tr><td>Total Test</td><td>Total Succ</td><td>Total Fail</td><td>First Fail Case</td><td>First Fail Reason</td><td>First Fail Time</td></tr>"));
  buf+=Scenario::Conv2Std(wxString::Format(wxT("<tr><td>%d</td><td>%d</td><td>%d</td>"),stGlobal.TotalRun,stGlobal.TotalSucc,stGlobal.TotalFail));
  if (stGlobal.FirstFail){
    wxString wxbuf=wxString::Format(wxT("<td>%s</td><td>%s</td><td>%s</td></tr>"),
				    GlobalFirstFail->GetIdentifier().c_str(),
				    stGlobal.FirstFailReason.c_str(),
				    TimeToStr(stGlobal.FirstFailTime).c_str());
    buf+=Scenario::Conv2Std(wxbuf);
  }
  else{
    buf+="<td></td><td></td><td></td></tr>";
  }
  buf+="</table>";
  buf+=Scenario::Conv2Std(_("<h2>Scenario Report</h2>"));
  buf+="<table cellpadding=\"7\" cellspacing=\"1\" border=\"1\" width=\"100%\">";
  buf+=Scenario::Conv2Std(_("<tr><td>Case</td><td>Total Test</td><td>Total Succ</td><td>Total Fail</td><td>First Fail Reason</td><td>First Fail Time</td></tr>"));
  for(ScePieceHash::iterator iter = ret.begin(); iter != ret.end(); iter++)
  {
    buf+=Scenario::Conv2Std(wxString::Format(wxT("<td>%s</td>"),
					     ((Scenario*)(iter->first))->GetIdentifier().c_str()));
    buf+=Scenario::Conv2Std(wxString::Format(wxT("<td>%d</td><td>%d</td><td>%d</td>"),iter->second.TotalRun,iter->second.TotalSucc,iter->second.TotalFail));

    if (iter->second.FirstFail){
      wxString wxbuf=wxString::Format(wxT("<td>%s</td><td>%s</td></tr>"),
				      iter->second.FirstFailReason.c_str(),
				      TimeToStr(iter->second.FirstFailTime).c_str());
      buf+=Scenario::Conv2Std(wxbuf);
    }
    else{
      buf+="<td></td><td></td></tr>";
    }
  }
  buf+="</table>";

  buf+=Scenario::Conv2Std(_("<h2>User Defined Statistic</h2>"));;
  buf+="<table cellpadding=\"7\" cellspacing=\"1\" border=\"1\" width=\"100%\">";
  {
	  for (StrLongHash::iterator iter=UserStatistic.begin(); iter!=UserStatistic.end();iter++){
		buf+="<tr>";
		buf+=Scenario::Conv2Std(wxString::Format(wxT("<td>%s</td><td>%ld</td>"),iter->first.c_str(),iter->second));
		buf+="</tr>";
	  }
  }
  buf+="</table>";

  buf+=Scenario::Conv2Std(_("<h2>Max value Statistic</h2>"));;
  buf+="<table cellpadding=\"7\" cellspacing=\"1\" border=\"1\" width=\"100%\">";
  {
	  for (Str_NumDate_Hash::iterator iter=MaxValus.begin(); iter!=MaxValus.end();iter++){
		buf+="<tr>";
		buf+=Scenario::Conv2Std(wxString::Format(wxT("<td>%s</td><td>%ld</td><td>%s</td>"),iter->first.c_str(),iter->second.Num,
							 TimeToStr(iter->second.time).c_str()));
		buf+="</tr>";
	  }
  }
  buf+="</table>";

  DoGlobalMsgStatReport(buf);

  buf+=Scenario::Conv2Std(_("<h2>Msg Statistic(without retransmit message)</h2>"));;
  buf+=Scenario::Conv2Std(_("<h3>Receive Msg Statistic</h3>"));;
  buf+="<table cellpadding=\"7\" cellspacing=\"1\" border=\"1\" width=\"100%\">";
  {
	  for (StrLongHash::iterator iter=RcvMsgStatistic.begin(); iter!=RcvMsgStatistic.end();iter++){
		buf+="<tr>";
		buf+=Scenario::Conv2Std(wxString::Format(wxT("<td>%s</td><td>%ld</td>"),iter->first.c_str(),iter->second));
		buf+="</tr>";
	  }
  }
  buf+="</table>";

  buf+=Scenario::Conv2Std(_("<h3>Sent Msg Statistic</h3>"));;
  buf+="<table cellpadding=\"7\" cellspacing=\"1\" border=\"1\" width=\"100%\">";
  {
	  for (StrLongHash::iterator iter=SntMsgStatistic.begin(); iter!=SntMsgStatistic.end();iter++){
		buf+="<tr>";
		buf+=Scenario::Conv2Std(wxString::Format(wxT("<td>%s</td><td>%ld</td>"),iter->first.c_str(),iter->second));
		buf+="</tr>";
	  }
  }
  buf+="</table>";

  buf+=Scenario::Conv2Std(_("<h2>Scearios Log Trace:</h2>"));
  for (size_t i=0;i<SceLogs.size();i++){
      buf+= Scenario::Conv2Std(wxString::Format(wxT("<div id=main%d style=color:blue onclick=document.all.child%d.style.display=(document.all.child%d.style.display=='none')?'':'none'>"),i,i,i));
    buf+=Scenario::Conv2Std(wxString::Format(_("<h3> + Scearios Log Trace %d</h3>"),i+1));
    buf+="</div>";

    buf+=Scenario::Conv2Std(wxString::Format(wxT("<div id=child%d style=display:none>"),i));
    buf+="<PRE>";
    SceLogs[i].Replace(wxT("<"),wxT("&lt;"));
    SceLogs[i].Replace(wxT(">"),wxT("&gt;"));
    buf+=Scenario::Conv2Std(SceLogs[i]);
    buf+="</PRE>";
    buf+="</div>";
  }

  buf+="</body>";
  f.Write(buf.c_str(),buf.size());
}

void SttScenarioControl::Quit(){
    Terminated=true;
    if (threadpool){
	threadpool->Wait();
	delete(threadpool);
	threadpool=NULL;
    }

    if (report) {
	DoReport();
	//report->SaveToReport();
	delete(report);
    }

    for (size_t i=0;i<AllTrans.size();i++){
	if (wxGetApp().AllTranses.IsAlive(AllTrans[i])){
	    wxGetApp().AllTranses.RemoveTrans(AllTrans[i]);//=false;
	}
    }

}

SttScenarioControl::~SttScenarioControl(){

	{//Release All term Cb
		TCPtrArray::iterator iter;
		for (iter=AllTerms.begin();iter!=AllTerms.end();iter++){
			TermControl*p = *iter;
			delete(p);
		}
		AllTerms.clear();
		AllTerms.Shrink();
	}

	{//Release All Varible Group
		CIPtrArray::iterator iter;
		for (iter=AllCallInfos.begin();iter!=AllCallInfos.end();iter++){
			CallInfo *p = *iter;
			delete(p);
		}
		AllCallInfos.clear();
		AllCallInfos.Shrink();
	}

	AllTrans.clear();
	//m_EvtQueue.clear();

}

